/*
 * Copyright (c) 2011 Sapienza University of Rome.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the
 *   distribution.
 * - Neither the name of the Sapienza University of Rome nor the names of
 *   its contributors may be used to endorse or promote products derived
 *   from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL SAPIENZA
 * UNIVERSITY OF ROME OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @author Ugo Colesanti <colesanti@dis.uniroma1.it>
 * @version 1.01 (October 10, 2011)
 *
 */
#warning "********* DISSense Mode - NODE *************"
#ifdef RELAY_NODE
#warning "********* RELAY NODE *************"
#endif
module NodeApplicationC{

  provides{
	  interface StdControl ;
  }
  uses {
    // Interfaces for initialization:
#ifndef TOSSIM
    interface Boot;
#endif
    interface SplitControl as RadioControl;
    interface StdControl as ManagerControl ;

    interface Send;
    interface Receive ;

    interface Leds;

	interface Timer<TMilli> as Timer ;

	interface DISSenseSampling ;
	interface DISSenseStatistics ;

  }

}

implementation {

	task void sendPacket() ;
	task void startApplication() ;

	uint16_t seqno ;
	message_t app_msg_buf ;
	bool send_busy = FALSE ;

	bool started = FALSE ;


	command error_t StdControl.start(){
		seqno = 0 ;
		started = TRUE ;
		if (call RadioControl.start() != SUCCESS) {
			// do nothing
		}
		return SUCCESS ;
	}

	command error_t StdControl.stop(){
		started = FALSE ;
		return SUCCESS ;
	}

#ifndef TOSSIM
	event void Boot.booted(){
		seqno = 0 ;
		started = TRUE ;
		if (call RadioControl.start() != SUCCESS) {
			// do nothing
		}
	}
#endif

	event void RadioControl.startDone(error_t error){
		if(started){
			if (error != SUCCESS){
				return ; // do nothing
			}
			post startApplication() ;
		}
	}

	task void startApplication(){
		dbg("Application","Node application started\n") ;

		// Starting DISSense
		call ManagerControl.start() ;


		if (TOS_NODE_ID != 0){

// The application of a relay node do nothing, it just starts DISSense
#ifndef RELAYNODE
			// Determines how much time before a DISSense active period the sampling need to be taken
			// WARNING: the sampling offset must be lower than the sampling period!!
			call DISSenseSampling.setSamplingOffset(SAMPLING_OFFSET) ;
#endif
		}
	}


	// This Timer is used to take samples from sensors even if the node is isolated (hence, DISSenseSampling.startSampling won't be signaled)
	// It is useful for local logging
	// If the node has a valid DISSense schedule, the DISSenseSampling.syncNotification(TRUE) will be signaled and will delete this Timer before it fires
	event void Timer.fired(){
		// put logging task here if needed
	    call Timer.startOneShot(call DISSenseStatistics.getSP()*1024UL) ; // keeps the sampling period
	}


	event void RadioControl.stopDone(error_t error){}



	  task void sendPacket(){
		  if(!send_busy){
			  uint8_t datareadings ;
				  app_data_t* app_data = (app_data_t *) call Send.getPayload(&app_msg_buf, sizeof(app_data_t));
				  if(app_data != NULL){

					  app_data->seqno = seqno ;
					  app_data->voltage = 1111 ;
					  app_data->timestamp = call Timer.getNow() ;
					  app_data->sensor_timer = 2222 ;
					  for(datareadings = 0; datareadings < SENSOR_COUNT; datareadings++){
						  app_data->sensor[datareadings].data = 3333 ;
						  app_data->sensor[datareadings].temp = 4444 ;
					  }

					  if (call Send.send(&app_msg_buf, sizeof(app_data_t)) == SUCCESS){
						  send_busy = TRUE ;
					  }
					  else{
						  seqno++ ;
					  }
				  }
		  }
		  else{
			  seqno++ ;
		  }
	 }

	  event void Send.sendDone(message_t* msg, error_t error) {
		  if(started){
			  if(error == SUCCESS) call Leds.led2Toggle() ;
			  else call Leds.led1Toggle() ;
				  send_busy = FALSE ;
				  seqno++ ;
		  }
	  }

	  event message_t* Receive.receive(message_t* msg, void *payload, uint8_t len) {return msg;} // Should be removed

	  event void DISSenseSampling.startSampling() {
		  dbg("Application","DISSenseSampling start sampling\n") ;
#ifndef RELAY_NODE
		  if(started){
			  if(!send_busy){ // If I was in out of sync, it is possible that the sampling has already been performed and that there is no need to perform another sampling task.
				  call Leds.led1Toggle() ;
				  post sendPacket() ; // we send a dummy packet, otherwise here need to put a startAcquisition task
			  }
			  call Timer.startOneShot(call DISSenseStatistics.getSP()*1024UL) ;
			  // here I have to start a timer with the sampling period of DISSense in order to schedule a sampling task if I'am not ready.
		  }
#endif
	  }



	  event void DISSenseSampling.syncNotification(bool inSync){ // always signal the status of inSync such that I can delete the samplingtimer (wait for startSampling)
		  dbg("Application","Sync notification received\n") ;
		  if(started){
			  if(inSync){
				  dbg("Application", "InSync\n") ;
				  if(call Timer.isRunning()){
					  dbg("Application","Deleting timer\n") ;
					  call Timer.stop() ;
				  }
			  }
		  }
	  }

}
